#include "MephistoCAM.h"

// 直接控制AXI/GPIO接口的寄存器，输出一个脉冲给PL端的CCD驱动模块
void Trig_Readout( uint32_t *gpio_reg_ctrl ){
    clrbit(*gpio_reg_ctrl, BIT_READOUT_TRIG);
    usleep(10);
    
    setbit(*gpio_reg_ctrl, BIT_READOUT_TRIG);
    usleep(10);
    
    clrbit(*gpio_reg_ctrl, BIT_READOUT_TRIG);
}



// ccd_readout发出信号驱动CCD读出，并可以根据传递的opt参数决定是否保存图像数据，以及如何保存
int ccd_readout(    uint32_t *gpio_reg_ctrl, 
                    uint32_t *gpio_reg_status, 
                    uint32_t *gpio_reg_pl_ps_ddr_cnt, 
                    uint32_t n_row,
                    uint32_t n_col,
                    int fast_read_opt,
                    char *filename ){

// #if defined( _DEBUG_READOUT_ )
//         printf("> n_row = %10d\n", n_row);
//         printf("> n_col = %10d\n", n_col);
// #endif

    uint32_t base_addr = 0x20000000;    // 512MByte 特殊地址值！！！！

    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    // uint32_t mem_size_in_Bytes = 1248*4666*16*4;   // twice of the actual size
    uint32_t mem_size_in_Bytes = 1248*4666*16*2;   // twice of the actual size


    FILE *fb = NULL;
    uint32_t *mem_ptr = mmap(NULL, mem_size_in_Bytes, PROT_READ | PROT_WRITE, MAP_SHARED, fd, base_addr);

    // Trig_Readout( gpio_reg_ctrl );

    if( fast_read_opt == 1 ){

        printf("> running is fast readout mode ...\n");

        uint32_t row_cnt = 0;
        uint32_t current_row = 0;
        uint32_t htrans_cnt_ps = 0;

        uint32_t pixel_num_in_a_row = 16 * n_col; // -->39kB

        fb = fopen(filename, "wba");   // append mode

#if defined( _DEBUG_READOUT_ )
        FILE *fp_debug_log = fopen("fast_readout_debug.log", "w");
#endif

        int row_cnt_bak = row_cnt;

        Trig_Readout( gpio_reg_ctrl );

        while( 1 ){

            // 判断PL端是否写完一行的数据
            if( 1 == getbit(*gpio_reg_status, BIT_DDR_DATA_RDY) ){
                
                current_row     = (*gpio_reg_pl_ps_ddr_cnt) & 0x0000ffff;   // 从PL端获取当前行数

                // 与PS端记录的行数对比，若PS端记录的行数小于PL端当前的行数，表示这一行数据还没有被PS端从DDR取出并保存
                if( row_cnt < current_row ){
                // if( row_cnt < 10 ){
                    // 开始保存数据
                    // uint32_t *ptr = mem_ptr + (pixel_num_in_a_row/2)*(current_row-1);   // 2个像元的数据组成一个32bits，所以这里除以2
                    // fwrite( (void *)ptr, sizeof(uint16_t), pixel_num_in_a_row, fb );

                    // 通知PL端，PS端已经完成从DDR中取出数据保存至磁盘
                    // usleep(10); // for debug
                    setbit( *gpio_reg_ctrl, BIT_PS_DATA_SAVED );

#if defined( _DEBUG_READOUT_)
                    // 输出PL端的计数器值，用于分析PS端完成任务是否及时
                    htrans_cnt_ps   = (*gpio_reg_pl_ps_ddr_cnt) >> 16;          // 获取PL端的htrans_cnt_ps
                    fprintf(fp_debug_log, "%10d\n", htrans_cnt_ps);
#endif

                    printf("(debug)> row_cnt = %6d, current_row = %d\n", row_cnt, current_row);

                    // 完成保存后更新PS端的row_cnt
                    row_cnt++;

                    if( row_cnt == n_row ) break;
                }
               

                // usleep(10); // for debug
                // setbit( *gpio_reg_ctrl, BIT_PS_DATA_SAVED );
                // row_cnt++;
                // printf("(debug)> row_cnt = %6d, current_row = %d\n", row_cnt, current_row);

                // usleep(10);
                // clrbit( *gpio_reg_ctrl, BIT_PS_DATA_SAVED );
            }
            // printf("(debug)> row_cnt = %6d, current_row = %d\n", row_cnt, current_row);

            // if( row_cnt >= n_row )
            // if( current_row >= n_row )
            //     break;

            usleep(5);  // 休眠时间可以经过实测之后做些调节
        }


#if defined( _DEBUG_READOUT_ )
        fclose(fp_debug_log);
#endif

        printf("> finished readout & save data ...\n");
    } else {

        // 当fast_read_opt!=1时，回到旧版本的读出方式，将BIT_PS_DATA_SAVED设为1，PL端CCD驱动模块中的状态机就按照旧版本的方式运行
        setbit( *gpio_reg_ctrl, BIT_PS_DATA_SAVED );

        Trig_Readout( gpio_reg_ctrl );
        
        time_t t_start, t_end;
        time(&t_start);

        uint rd_is_done = getbit( *gpio_reg_status, BIT_READOUT_DONE );
        
        printf("[CCD_READOUT] --> start\n");

        // 判断CCD读出是否已经完成
        while( rd_is_done != 1 ){

            rd_is_done = getbit(*gpio_reg_status, BIT_READOUT_DONE);

            if( rd_is_done == 1 ){
                printf("[CCD_READOUT] --> finish!\n");
                time(&t_end);
                printf("[CCD_READOUT] --> used %ld seconds.\n", t_end - t_start);
                break;
            }

            // sleep(1);
            usleep(1000);
        }

        // 开始从DDR中取出数据
        fb = fopen(filename, "wb");
        printf("[CCD_READOUT] --> start transferring data from DDR to disk ...\n");
        time(&t_start);
        // printf("(debug)> n_row*n_col*2*16 = %d\n", n_row*n_col*2*16);
        // fwrite((void *)mem_ptr, sizeof(uint16_t), n_row*n_col*2*16, fb);
        fwrite((void *)mem_ptr, sizeof(unsigned char), n_row*n_col*2*16, fb);
        time(&t_end);
        printf("[CCD_READOUT] --> transferring data from DDR to disk used %ld seconds.\n", t_end - t_start);
    }

    if( fb != NULL )
        fclose(fb);

    munmap(mem_ptr, mem_size_in_Bytes);
    close(fd);

    // DropCache();    //  清空缓存
    DropCaches(3);    //  清空缓存

    return 0;
}

// ccd_exposure在ccd_readout的基础上增加了对快门的控制，其余功能一致
int ccd_exposure(   double t_exp, 
                    uint32_t *gpio_reg_ctrl, 
                    uint32_t *gpio_reg_status, 
                    uint32_t *gpio_reg_pl_ps_ddr_cnt, 
                    uint32_t n_row,
                    uint32_t n_col,
                    int fast_read_opt,
                    char *filename ){

    /* open shutter for a given time interval and then close it */
    bonn_shutter_exposure(  gpio_reg_ctrl, 
                            gpio_reg_status,
                            t_exp );
    
    /* readout */
    ccd_readout(    gpio_reg_ctrl, 
                    gpio_reg_status, 
                    gpio_reg_pl_ps_ddr_cnt,
                    n_row,
                    n_col,
                    fast_read_opt,
                    filename );

    return 0;
}

int DropCaches( int drop ){
    int ret = 0;
    
    int fd = open("/proc/sys/vm/drop_caches", O_RDWR);
    if( fd < 0 ){
        return -1;
    }
    
    char dropData[32] = {0};
    int dropSize = snprintf(dropData, sizeof(dropData), "%d", drop);
    
    ret = write(fd, dropData, dropSize);
    close(fd);
    
    return ret;
}

int DropCache(){
    
    int drop_ret;

    int opt;
    
    for( opt=1; opt<=3; opt++ ){
        drop_ret = DropCaches(3);
        
        if( drop_ret < 0 ){
            printf("# ERROR\n");
            printf("# src: %s func: %s L: %d, failed to drop caches (3) \n", __FILE__, __func__, __LINE__);
        } else {
            printf("# --> drop cache (%d) done.\n", opt);
        }
    }
    
    return 0;
}